package de.mssp.sensorkit.bluetooth;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.UUID;
import de.mssp.sensorkit.R;
import android.app.Activity;
import android.app.ListActivity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;

public class BluetoothActivity extends ListActivity  implements OnClickListener
{
	// Intent request codes
    private static final int REQUEST_ENABLE_BT = 2;
    private static final int REQUEST_MESSAGE = 3;
    
	BluetoothAdapter mBluetoothAdapter;
	BluetoothDeviceAdapter mDeviceAdapter;
	Button scanButton;
	BroadcastReceiver mReceiver;	
	ServerThread server;
	String mMessage;
	HashMap<String,BluetoothSocket>sockets;
	BluetoothServerSocket serverSocket;
	
	//Shows our Toast-Message from other Threads
	Handler mToastHandler = new Handler(){
		@Override
		public void handleMessage(Message m) {
		Toast.makeText(getBaseContext(), m.getData().getString("message"), Toast.LENGTH_LONG).show();
		}
		};
	
	UUID mUUID;
	
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.bluetooth);
		
		mUUID = UUID.fromString("f5efb9d2-deaf-4af8-a304-8739fe2109ab");
		sockets = new HashMap<String,BluetoothSocket>();
		mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
		
		if (mBluetoothAdapter != null) 
		{
			//Register the Listener
			scanButton = (Button) findViewById(R.id.bt_button_scan);
			scanButton.setOnClickListener(this);
			
			ListView lv = getListView();
			lv.setTextFilterEnabled(true);
			
			lv.setOnItemClickListener(new OnItemClickListener()
			{
				 public void onItemClick(AdapterView<?> parent, View view,int position, long id)
				 {
					 BluetoothDevice selectedDevice = mDeviceAdapter.getItem(position);
				     switch(selectedDevice.getBondState())
				     {
				     	case BluetoothDevice.BOND_NONE:
				     	{
				    	 try{
				    		 //Try to establish a connection
				    		 Toast.makeText(getApplicationContext(), "Verbindung wird hergestellt.", Toast.LENGTH_SHORT).show();
				    		 // MY_UUID is the app's UUID string, also used by the server code
					            BluetoothSocket socket = selectedDevice.createRfcommSocketToServiceRecord(mUUID);
					            socket.connect();
					            sockets.put(selectedDevice.getAddress(), socket);
					            Toast.makeText(getApplicationContext(), "Verbindung erfolgreich hergestellt. \n Bitte erneut antippen, um eine Nachricht zu senden.", Toast.LENGTH_SHORT).show();
				    	 }
				    	 catch (IOException e) 
							{
								e.printStackTrace();
							}
				     	}
				     	break;
				     	case BluetoothDevice.BOND_BONDING:
				     		//currently connecting
				     		Toast.makeText(getApplicationContext(), "Bitte warten, eine Verbindung wird gerade aufgebaut.", Toast.LENGTH_SHORT).show();
				     		break;
				     	case BluetoothDevice.BOND_BONDED:
				     	{
				     		//Connection established, ready to send messages
				     		Toast.makeText(getApplicationContext(), "Verbindung vorhanden. Nachricht kann gesendet werden.", Toast.LENGTH_SHORT).show();
				     		Intent msgIntent = new Intent(getApplicationContext(), MessageActivity.class);
				     		msgIntent.putExtra("device", selectedDevice);
				     		startActivityForResult(msgIntent, REQUEST_MESSAGE);
				     	}
				     }
				     
				    }
			});
			
			mDeviceAdapter = new BluetoothDeviceAdapter(this, R.layout.row_bt, new ArrayList<BluetoothDevice>());
			setListAdapter(mDeviceAdapter);

			try 
			 {
				 serverSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord("Sensorkit",mUUID);
				 server = new ServerThread(mToastHandler);
		         server.start();
		     } catch (IOException e) { 
		    	 
		     }
		}
	}
	
	/** Sends a message to the passed BluetoothDevice */
	private void sendMessageToDevice(String message, BluetoothDevice device) 
	{
		//Device already paired
		if(sockets.containsKey(device.getAddress()))
		{
			BluetoothSocket socket = sockets.get(device.getAddress());
			
			try {
				OutputStream stream = socket.getOutputStream();
				stream.write(message.getBytes());
				stream.flush();
				
			} catch (IOException e) 
			{
				e.printStackTrace();
			}
		//device not paired, create new connection
		}else{
			try 
			{
	            // MY_UUID is the app's UUID string, also used by the server code
	            BluetoothSocket socket = device.createRfcommSocketToServiceRecord(mUUID);
	            socket.connect();
	            sockets.put(device.getAddress(), socket);
	          //Handler is necessary to "ship" the message between Threads
	            new Client(socket, mToastHandler).start();
				OutputStream stream = socket.getOutputStream();
				stream.write(message.getBytes());
				stream.flush();

	            
	        } catch (IOException e) { 
	        	e.printStackTrace();
	        }
		}
	}
	
	/**Initializing the device for Bluetooth-Connections
	 * If devices are found while scanning, they're added to the ListView  */
	public void setupBluetooth()
	{
		//Bluetooth not enabled, start request to enable it
		if (!mBluetoothAdapter.isEnabled()) 
		{
		    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
		    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
		}
		
		// Create a BroadcastReceiver for ACTION_FOUND
		 mReceiver = new BroadcastReceiver() {
		    public void onReceive(Context context, Intent intent) {
		        String action = intent.getAction();
		        // When discovery finds a device
		        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
		            // Get the BluetoothDevice object from the Intent
		            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
		            // Add the name and address to an array adapter to show in a ListView
		            if(!mDeviceAdapter.contains(device)){
		            	mDeviceAdapter.add(device);
		            	mDeviceAdapter.notifyDataSetChanged();
		            }
		        }
		        else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
		        	//no devices found
		        	if(mDeviceAdapter.getCount() == 0)
		        		Toast.makeText(getApplicationContext(), "No devices found!", Toast.LENGTH_SHORT).show();
		        }
		    }
		};
		// Register the BroadcastReceiver
		IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
		// Don't forget to unregister during onDestroy
		registerReceiver(mReceiver, filter); 
		filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
		registerReceiver(mReceiver, filter);
	}
	
	/**Start a scan for new Bluetooth devices */
	public void scanBluetoothDevices()
	{
		if(!mBluetoothAdapter.isDiscovering())
		{
			mDeviceAdapter.clear();
			mDeviceAdapter.notifyDataSetChanged();
			mBluetoothAdapter.startDiscovery();
		}
	}
	
	@Override
	public void onStart(){
		super.onStart();
		
		//Start Scanning for Devices
		setupBluetooth();

		/*
		Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
		// If there are paired devices
		if (pairedDevices.size() > 0) {
		    // Loop through paired devices
		    for (BluetoothDevice device : pairedDevices) {
		        // Add the name and address to an array adapter to show in a ListView
		        mDeviceAdapter.add(device);
		    }
		}*/
		
	}
	
	/** Callback from startActivityForResult() */
	 public void onActivityResult(int requestCode, int resultCode, Intent data) 
	 {	 
		 switch(requestCode)
		 {
		 	case REQUEST_ENABLE_BT:
		 	{
		 		// When the request to enable Bluetooth returns
	            if (resultCode == Activity.RESULT_OK) {
	                // Bluetooth is now enabled, so set up a chat session
	            	Toast.makeText(this, "BT successfully enabled", Toast.LENGTH_LONG).show();
	               // setupChat();
	            } else {
	                // User did not enable Bluetooth or an error occured
	                //Log.d(TAG, "BT not enabled");
	                Toast.makeText(this, "BT not enabled, leaving App!", Toast.LENGTH_SHORT).show();
	                finish();
	            }
		 	}
		 	break;
		 	case REQUEST_MESSAGE:{
		 		if(resultCode == Activity.RESULT_OK){
		 			//Get the message
		 			mMessage = data.getExtras().getString("message");
		 			//get selected device
		 			BluetoothDevice device = (BluetoothDevice) data.getExtras().get("device");
		     		sendMessageToDevice("Message from " + device.getName() + ": \n" + mMessage, device);
		 		}
		 		else
		 			mMessage = "";
		 	}
		 }
		 //Done
	 }

	 /** OnClick handler */
	public void onClick(View v) 
	{
		if(v.equals(scanButton))
			this.scanBluetoothDevices();
	}
	
	@Override
	public void onResume()
	{
		super.onResume();
		this.scanBluetoothDevices();
		//if(server.isAlive())
			//server.resume();
	}
	
	@Override
	public void onPause()
	{
		super.onPause();
		
		if(mBluetoothAdapter.isDiscovering())
			mBluetoothAdapter.cancelDiscovery();
		
		//server.suspend();
	}
	
	@Override
	public void onStop(){
		
		super.onStop();
		if(mBluetoothAdapter.isDiscovering())
			mBluetoothAdapter.cancelDiscovery();
	}
	
	@Override
	public void onDestroy()
	{
		super.onDestroy();
		unregisterReceiver(mReceiver);
		//server.stop();
		server.destroy();
		try {
			serverSocket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.option_menu, menu);
        return true;
    }
	
	@Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.discoverable:
            // Ensure this device is discoverable by others
            ensureDiscoverable();
            return true;
        }
        return false;
    }
	
	/** Ensure that this device can be found by other scanning devices by starting 
	 * a new Intent
	 */
	 private void ensureDiscoverable() {
	        if (mBluetoothAdapter.getScanMode() !=
	            BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
	            Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
	            discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
	            startActivity(discoverableIntent);
	        }
	    }

	private class ServerThread extends Thread 
	{
		Handler mToastHandler;
		
		//redirects our Client-Message to the main Thread
		Handler mServerHandler = new Handler(){
			@Override
			public void handleMessage(Message m) {
				//Make a message for the Handler
                Message msg = mToastHandler.obtainMessage();
                msg.setData(m.getData());
                mToastHandler.sendMessage(msg);
			}
			};
		
		
		public ServerThread(Handler h){
			this.mToastHandler = h;
		}
		
	    public void run() 
	    {
	        BluetoothSocket socket = null;
	        // Keep listening until exception occurs or a socket is returned
	        while (true) 
	        {
	            try 
	            {
	                socket = serverSocket.accept();
	            } catch (IOException e) {
	                break;
	            }
	            // If a connection was accepted
	            if (socket != null) 
	            {
	                // Do work to manage the connection (in a separate thread)
	            	//Handler is necessary to "ship" the message between Threads
	            	new Client(socket, this.mServerHandler).start();
	                sockets.put(socket.getRemoteDevice().getAddress(), socket);
	            }
	        }
	    }
	}
	
	private class Client extends Thread 
	{
		BluetoothSocket socket;
		Handler msgHandler;
		
		
		public Client(BluetoothSocket s, Handler h)
		{
			super();
			this.socket = s;
			this.msgHandler = h;
			
		}
		
		public void run() 
		{
	        byte[] buffer = new byte[1024];  // buffer store for the stream
	        int bytes; // bytes returned from read()
	        InputStream mmInStream = null;
			
	        try {
				mmInStream = socket.getInputStream();
			
			} catch (IOException e1) {
				e1.printStackTrace();
			}
	        // Keep listening to the InputStream until an exception occurs
	        while (true) {
	            try {
	                // Read from the InputStream
	                bytes = mmInStream.read(buffer);
	                
	                //Make a message for the Handler
	                Message msg = msgHandler.obtainMessage();
	                Bundle b = new Bundle();
	                b.putString("message", new String(buffer,0,bytes));
	                msg.setData(b);
	                msgHandler.sendMessage(msg);
	                
	                
	                Log.i("Buffer:", new String(buffer,0,bytes));
	            } catch (IOException e) {
	                break;
	            }
	        }
	    }

	}
}
